// 保存构造函数引用，避免import，避免打包的时候
let Vue
// 通过Store访问到类class Store
class Store {
  constructor(options) {
    // 需要用options必须赋值，不能直接用
    // this.$options = options;
    // 保存用户传入的emutations
    this._mutations = options.mutations
    this._actions = options.actions
    this._wrappedGetters = options.getters

    // 定义component选项
    const computed = {}
    this.getters = {}
    // {doubleCounter(state){}}
    // computed是没有参数的，但是传入的是有参数的
    Object.keys(this._wrappedGetters).forEach((key) => {
      // 获取用户定义的getter
      const fn = store._wrappedGetters[key]
      // 转换为computed可以使用的无参数形式，函数柯里化+用store的state传参就好了！
      computed[key] = funtion () {
        return fn(store.state)
      }
      // 为getters定义只读属性
      Object.defineProperty(store.getters,key,{
        // 于是通过this.vm可以访问到相关的方法
        get: ()=>store._vm[key]
      })
    })
    // 响应化处理state
    // this.state = new Vue({
    //   data: options.state
    // })
    this._vm = new Vue({
      data: {
        // 加两个$，Vue不做代理，对外部隐藏（或者严格模式）
        $$state: options.state,
      },
      computed: {},
    })

    // 绑定commit、dispatch的上下文为store实例，否则有可能this指向就乱了。于是异步调用的this也ok了
    this.commit = this.commit.bind(this)
    this.dispatch = this.dispatch.bind(this)
  }

  // 存取器，实现对state的保护，用户通过 store.state访问存取器
  get state() {
    console.log(this._vm)

    return this._vm._data.$$state
  }
  // 只读的
  set state(v) {
    console.error('你造吗？你这样不好！')
  }

  // store.commit('add', 1)
  // type: mutation的类型
  // payload：载荷，是参数
  commit(type, payload) {
    // 把type传入的参数(键值对)找到
    const entry = this._mutations[type]
    if (entry) {
      entry(this.state, payload)
    }
  }
  // type是一个上下文对象，所以第一个参数传入this
  dispatch(type, payload) {
    const entry = this._actions[type]
    if (entry) {
      entry(this, payload)
    }
  }
}

function install(_Vue) {
  Vue = _Vue
  // install需要挂载一下，不然不能全局访问到
  // vue调用install就把自己的当成参数传递进去
  Vue.mixin({
    beforeCreate() {
      if (this.$options.store) {
        Vue.prototype.$store = this.$options.store
      }
    },
  })
}

// Vuex
// 访问的时候是Vuex.store
// Vue.use要求实现install方法
export default {
  Store,
  install,
}
